package org.terramagnet.mvcframework.resource.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.terramagnet.mvcframework.resource.ResourceService;
import org.terramagnet.mvcframework.tools.SpringMvcUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;
import javax.annotation.PostConstruct;
import javax.servlet.ServletContext;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.multipart.MultipartFile;
import org.terramagnet.mvcframework.ApplicationRuntimeException;
import org.terramagnet.mvcframework.friendly.I18nFriendly;
import org.terramagnet.mvcframework.http.Connection;
import org.terramagnet.mvcframework.http.HttpFacade;
import org.terramagnet.mvcframework.resource.ResourceArchive;
import org.terramagnet.mvcframework.tools.Paginal;

/**
 * 远程资源.
 *
 * <p>
 * 不要使用路径扫描，直接声明bean：</p>
 * <pre><code>&lt;bean class="org.terramagnet.mvcframework.resource.service.RemoteResourceService"/&gt;</code></pre>
 *
 * @author lip
 */
public class RemoteResourceService implements ResourceService {

    private static final Pattern schema = Pattern.compile("^(http|https|ftp)://.*");
    private final ObjectMapper jsonUtil = new ObjectMapper();
    @Value("#{config['resource.remote']}")
    private String api;
    @Autowired
    private ServletContext servletContext;

    @Override
    public ResourceArchive view(String key, boolean thumbnail) {
        return new ResourceArchive(null, requireRestApi(key));
    }

    @Override
    public List<String> create(String bucket, MultipartFile... multipartFiles) {
        try {
            Connection connection = HttpFacade.connect(requireApi())
                .data("bucket", bucket);
            for (MultipartFile multipartFile : multipartFiles) {
                connection.data("file", multipartFile.getOriginalFilename(), multipartFile.getInputStream());
            }
            return Arrays.asList(connection.post().split(","));
        } catch (IOException ex) {
            throw new ApplicationRuntimeException(ex, I18nFriendly.DEFAULT);
        }
    }

    @Override
    public String create(String bucket, String filename, InputStream inputStream) {
        return create(bucket, new SimpleMultipartFile(filename, inputStream)).get(0);
    }

    @Override
    public void delete(String... keys) {
        try {
            if (keys == null) {
                throw new NullPointerException("删除的资源不能为null");
            }
            if (keys.length == 1) {
                HttpFacade.connect(requireRestApi(keys[0]))
                    .method(Connection.Method.DELETE)
                    .execute();
            } else {
                HttpFacade.connect(requireApi())
                    .data("keys", StringUtils.join(keys, ","))
                    .method(Connection.Method.DELETE)
                    .execute();
            }
        } catch (IOException ex) {
            throw new ApplicationRuntimeException(ex, I18nFriendly.DEFAULT);
        }
    }

    @Override
    public void link(String key, String module, String principal) {
        try {
            HttpFacade.connect(requireRestApi(key))
                .data("module", module)
                .data("principal", principal)
                .method(Connection.Method.PUT)
                .execute();
        } catch (IOException ex) {
            throw new ApplicationRuntimeException(ex, I18nFriendly.DEFAULT);
        }
    }

    /**
     * 判断资源路径是否是绝对URL.
     */
    @Override
    public boolean isAbsoluteUrl(String path) {
        return schema.matcher(path).matches();
    }

    /**
     * {@inheritDoc }
     *
     * <p>
     * TODO 尚未测试</p>
     *
     * @deprecated 尚未测试
     */
    @Override
    public Paginal<ResourceArchive> page(Boolean temporary, Date from, Date to, int page, int size) {
        try {
            return HttpFacade.connect(requireApi())
                .get(new Connection.ResponseCallable<Paginal<ResourceArchive>>() {

                    @Override
                    public Paginal<ResourceArchive> withBodyStream(InputStream inputStream) throws IOException {
                        return jsonUtil.readValue(inputStream, new TypeReference<Paginal<ResourceArchive>>() {
                        });
                    }
                });
        } catch (IOException ex) {
            throw new ApplicationRuntimeException(ex, I18nFriendly.DEFAULT);
        }
    }
    private String apiCache;

    private String requireApi() {
        if (apiCache == null) {
            if (StringUtils.isBlank(api)) {
                return null;
            }
            if (api.startsWith("../")) {
                apiCache = SpringMvcUtils.currentHost() + api.substring(2);
            } else {
                apiCache = api;
            }
        }

        if (apiCache == null) {
            throw new IllegalStateException("尚未配置远程资源服务器API地址-resource.remote");
        }
        return apiCache;
    }

    private String requireRestApi(String key) {
        String _api = requireApi();
        if (_api.endsWith("/") || key.startsWith("/")) {
            return _api + key;
        } else {
            return _api + "/" + key;
        }
    }

    public void setApi(String api) {
        this.api = api;
    }

    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    @PostConstruct
    public void startup() {
        if (servletContext != null) {
            servletContext.setAttribute("srx", requireApi());
        }
    }

    private class SimpleMultipartFile implements MultipartFile {

        private final String filename;
        private final InputStream inputStream;

        public SimpleMultipartFile(String filename, InputStream inputStream) {
            this.filename = filename;
            this.inputStream = inputStream;
        }

        @Override
        public String getName() {
            return filename;
        }

        @Override
        public String getOriginalFilename() {
            return filename;
        }

        @Override
        public String getContentType() {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean isEmpty() {
            return false;
        }

        @Override
        public long getSize() {
            throw new UnsupportedOperationException();
        }

        @Override
        public byte[] getBytes() throws IOException {
            throw new UnsupportedOperationException();
        }

        @Override
        public InputStream getInputStream() throws IOException {
            return inputStream;
        }

        @Override
        public void transferTo(File dest) throws IOException, IllegalStateException {
            throw new UnsupportedOperationException();
        }

    }

}
